org	0100h
	jmp loader_start
%include	"include/load.inc"
%include	"include/pm.inc"

;变量
wSectorNo				dw 0
wRootDirSizeForLoop    dw 14
event                   dw 0 
BPB_SecPerTrk	dw 18
BS_DrvNum	    db 0
BPB_BytsPerSec	dw 512
;GDT Table

LABEL_GDT:	    	Descriptor 0,            0, 0              					; 空描述符
LABEL_DESC_FLAT_C:  Descriptor 0,      0fffffh, DA_CR | DA_32   |DA_LIMIT_4K 	;0-4G
LABEL_DESC_FLAT_RW: Descriptor 0,      0fffffh, DA_DRW| DA_32   |DA_LIMIT_4K	;0-4G
LABEL_DESC_VIDEO:   Descriptor 0B8000h, 0ffffh, DA_DRW| DA_DPL3 				; 显存首地址

GdtLen		equ	$ - LABEL_GDT
GdtPtr		dw	GdtLen - 1				; 段界限
			dd	BaseOfLoaderPhyAddr + LABEL_GDT		; 基地址
;Selectore
SelectorFlatC		equ	LABEL_DESC_FLAT_C	- LABEL_GDT
SelectorFlatRW		equ	LABEL_DESC_FLAT_RW	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT + SA_RPL3

;常量宏

SectorNoOfRootDirectory	equ	19
DeltaSectorNo 			equ 17
RootDirSectors		    equ	14	
SectorNoOfFAT1          equ 1 
BaseOfStack 			equ 0100h
PageDirBase	equ	100000h	; 页目录开始地址: 1M
PageTblBase	equ	101000h	; 页表开始地址:   1M + 4K

loader_start:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, BaseOfStack
	push LoadMessage
	call Dispstr
	add sp, 2
	
	;加载内核

	xor ah, ah
    xor dl, dl
    int 13h

 	mov word [wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
    cmp word [wRootDirSizeForLoop], 0
    jz LABEL_NO_LOADERBIN 
    dec word [wRootDirSizeForLoop]
    mov ax, BaseOfKernelFile ;改动
    mov es, ax
    mov bx, OffsetOfKernelFile
    mov ax, [wSectorNo]
    mov cl, 1
    call ReadSector
	
	mov si, KernalFileName
	mov di, OffsetOfKernelFile
	cld
	mov dx, 10h ;一共扇区内有16个项
LABEL_SEARCH_FOR_LOADERBIN:
	cmp	dx, 0
	jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR
	dec dx
	mov cx, 11 ;待比较的字符串长度
LABEL_CMP_FILENAME:
	cmp cx, 0
	jz LABEL_FILENAME_FOUND
	dec cx
	lodsb
	cmp al, byte [es:di]
	jz LABEL_GO_ON
	jmp	LABEL_DIFFERENT
LABEL_GO_ON:
	inc di
	jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
	and di, 0FFE0h
	add di, 20h
	mov si, KernalFileName
	jmp LABEL_SEARCH_FOR_LOADERBIN
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
	add word [wSectorNo],1
	jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_LOADERBIN:
	mov ax, cs
	mov es, ax
	push NoFoundMessage
	call Dispstr
	add sp, 2
	jmp $

LABEL_FILENAME_FOUND:
	mov ax, RootDirSectors
	and di, 0FFE0h
	add di, 01Ah
	mov cx, word [es:di]	;得到FAT序号，也是
	push cx					;保存FAT序号
	add cx, ax
	add cx, DeltaSectorNo
	mov ax, BaseOfKernelFile
	mov es, ax
	mov bx, OffsetOfKernelFile
	mov ax, cx

LABEL_GOON_LOADING_FILE:
	mov cl, 1
	call ReadSector
	pop ax					;取出FAT序号
	call GetFATEntry
	cmp ax, 0FFFh
	jz LABEL_FILE_LOADED
	push ax
	mov dx, RootDirSectors
	add ax, dx
	add ax, DeltaSectorNo		;计算下一个扇区号
	add bx, [BPB_BytsPerSec]	;移动目的内存指针
	jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
	mov ax, cs
	mov es, ax


	;获取内存信息以便分页

    ;获取内存大小
    
    xor ebx, ebx
    mov di, _MemChk_Buff
.loop:
    mov eax, 0xE820
    mov ecx, 20
    mov edx, 0x534D4150
    int 0x15
    jc LABLE_MEM_CHK_FAIL
    add di, 20
    inc dword [_dwMCRNumber]
    cmp ebx, 0
    jne .loop
    jmp LABEL_MEM_CHK_OK
LABLE_MEM_CHK_FAIL:
    mov dword [_dwMCRNumber], 0
LABEL_MEM_CHK_OK:

	; 加载 GDTR
	lgdt	[GdtPtr]

	; 关中断
	cli

	; 打开地址线A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax
	; 真正进入保护模式
	jmp	dword SelectorFlatC:(BaseOfLoaderPhyAddr+LABEL_PM_START)

	jmp	$




Dispstr:
	mov bp, sp
	mov ax,word [bp+2]	;字符串的偏移地址
	push ax 			;保存字符串偏移地址
	mov bp, ax 
	xor cx,cx			; @TODO: 应该推入栈
.loop:
	mov al, [es:bp]
	test al, al
	jz .end
	inc cx
	inc bp
	jmp .loop
.end:
	pop bp;
	mov ax, 01301h
	mov bx, 0007h
	mov dl, 0
	mov dh, 3
	int 10h
	ret

ReadSector:
    push bp
    mov bp, sp
    sub esp, 2

    mov byte [bp-2], cl
    push bx
    mov bl, [BPB_SecPerTrk]
    div bl
    inc ah
    mov cl, ah	;求得物理的扇区号
    mov dh, al
    shr al, 1
    mov ch, al	;求得柱面
    and dh, 1	;求得磁头号
    pop bx
	mov	dl, [BS_DrvNum]
.GoOnReading:
	mov ah, 2
	mov al, byte [bp-2]
	int 13h
	jc .GoOnReading

	add esp, 2	;清除堆栈中的局部变量
	pop bp

	ret

GetFATEntry:
	push es
	push bx

	push ax
	mov ax, BaseOfKernelFile
	sub ax, 0100h
	mov es, ax
	pop ax
	mov byte [event], 0
	mov bx, 3
	mul bx 
	mov bx, 2
	div bx
	cmp dx, 0
	jz LABEL_EVENT
	mov byte [event], 1
LABEL_EVENT:
	xor dx, dx
	mov bx, [BPB_BytsPerSec]
	div bx
	push dx
	mov bx, 0
	add ax, SectorNoOfFAT1
	mov cl,2
	call ReadSector
	pop dx
	add bx, dx
	mov ax, [es:bx]
	cmp byte [event],1
	jnz LABEL_EVENT_2
	shr ax, 4
LABEL_EVENT_2:
	and ax,0FFFh

	pop	bx
	pop	es
	ret

[SECTION .s32]
ALIGN	32
[BITS	32]
LABEL_PM_START:
	mov	ax, SelectorVideo
	mov	gs, ax

	mov	ax, SelectorFlatRW
	mov	ds, ax
	mov	es, ax
	mov	fs, ax
	mov	ss, ax
	mov	esp, TopOfStack

	push dword 0x000f0400
	push dword OffsetTestStr
	call DisplayStr
	add esp, 8
	mov edi, (80*5+0)*2
	call DisplayMemRange

	push dword MemSize
	push dword [dwMCRNumber]
	push dword MemChk_buff
	
	call GetMaxMemRange

	mov eax, [MemSize]
    call DisplayReg
	
	call SetupPaging
	push dword 0x000f0c00
	push dword OffsetPageEnabled
	call DisplayStr

	;设置内核
	call InitKernel	
	jmp	SelectorFlatC:KernelEntryPointPhyAddr	;

	jmp $	

DisplayMemRange:
    push esi
    push ecx


    mov esi, MemChk_buff
    mov ecx, [dwMCRNumber] 
.loop:
    mov edx, 5
.1:
    mov eax, [esi] ;第一个32位
    call DisplayReg ;显示
    add edi,2 ;空格
    add esi,4
    dec edx
    cmp edx, 0
    jnz .1
    call DislpayReturn ;一行结束换行  
    loop .loop
    
    pop ecx
    pop esi
    ret

; GetMaxMemRange(void* buff,size_t n,int* dest)
GetMaxMemRange:
    push eax
    push ebx
    push esi
    push ecx

    mov esi, [esp+20]   ; buff
    mov ecx, [esp+24]   ; n
    mov ebx, [esp+28]   ; dest
.loop:
    cmp dword [esi+16], 1
    jnz .1
    mov eax, [esi]
    add eax, [esi+8]
    cmp [ebx],eax    ;*dest
    ja .1
    mov [ebx],eax
.1:
    add esi, 20
    loop .loop
    
    pop ecx
    pop esi
    pop ebx
    pop eax
    ret


SetupPaging:
	; 根据内存大小计算应初始化多少PDE以及多少页表
	xor	edx, edx
	mov	eax, [MemSize]
	mov	ebx, 400000h	; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
	div	ebx
	mov	ecx, eax	; 此时 ecx 为页表的个数，也即 PDE 应该的个数
	test	edx, edx
	jz	.no_remainder
	inc	ecx		; 如果余数不为 0 就需增加一个页表
.no_remainder:
	push	ecx		; 暂存页表个数

	; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

	; 首先初始化页目录
	mov	ax, SelectorFlatRW
	mov	es, ax
	mov	edi, PageDirBase	; 此段首地址为 PageDirBase
	xor	eax, eax
	mov	eax, PageTblBase | PG_P  | PG_USU | PG_RWW
.1:
	stosd
	add	eax, 4096		; 为了简化, 所有页表在内存中是连续的
	loop	.1

	; 再初始化所有页表
	pop	eax			; 页表个数
	mov	ebx, 1024		; 每个页表 1024 个 PTE
	mul	ebx
	mov	ecx, eax		; PTE个数 = 页表个数 * 1024
	mov	edi, PageTblBase	; 此段首地址为 PageTblBase
	xor	eax, eax
	mov	eax, PG_P  | PG_USU | PG_RWW
.2:
	stosd
	add	eax, 4096		; 每一页指向 4K 的空间
	loop	.2

	mov	eax, PageDirBase
	mov	cr3, eax
	mov	eax, cr0
	or	eax, 80000000h
	mov	cr0, eax
	jmp	short .3
.3:
	nop
	ret

InitKernel:
	xor esi, esi
	mov cx, word [BaseOfKernelFilePhyAddr+2Ch]
	movzx ecx, cx
	mov esi, [BaseOfKernelFilePhyAddr + 1Ch]
	add esi, BaseOfKernelFilePhyAddr
.Begin:
	mov eax, [esi]
	cmp eax, 0
	jz .NoAction
	push dword [esi + 010h]	;size
	mov eax, [esi + 04h]	;
	add eax, BaseOfKernelFilePhyAddr
	push eax	;源地址
	push dword [esi + 08h]	;目的地址
	call memcpy
	add esp, 12
.NoAction:
	add esi, 020h
	dec ecx
	jnz .Begin
	
	ret

%include "include/lib.inc"

[SECTION .data1]

ALIGN	32

;数据


KernalFileName db "KERNAL  BIN",0 ;内核文件名

LoadMessage: db "Loading...",0
ReadyMessage: db "Kernal Ready!",0
NoFoundMessage: db "No Kernal!",0

_testStr: db "Now! In protect mode!!!"
		  db 0
_page_enabled: 	db "Page enabled!"
				db 0

OffsetTestStr equ BaseOfLoaderPhyAddr + _testStr
OffsetPageEnabled equ BaseOfLoaderPhyAddr + _page_enabled
_dwMCRNumber: dd 0
_MemSize: dd 0
_MemChk_Buff: times 256 db 0
MemSize 	equ BaseOfLoaderPhyAddr + _MemSize
dwMCRNumber equ BaseOfLoaderPhyAddr + _dwMCRNumber
MemChk_buff equ BaseOfLoaderPhyAddr + _MemChk_Buff

StackSpace:	times	1024	db	0
TopOfStack	equ	BaseOfLoaderPhyAddr + $	; 栈顶
